<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=2">
<meta name="theme-color" content="#222">
<meta name="generator" content="Hexo 5.4.0">
  <link rel="apple-touch-icon" sizes="180x180" href="/codeblog/images/apple-touch-icon-next.png">
  <link rel="icon" type="image/png" sizes="32x32" href="/codeblog/images/favicon-32x32-next.png">
  <link rel="icon" type="image/png" sizes="16x16" href="/codeblog/images/favicon-16x16-next.png">
  <link rel="mask-icon" href="/codeblog/images/logo.svg" color="#222">

<link rel="stylesheet" href="/codeblog/css/main.css">


<link rel="stylesheet" href="/codeblog/lib/font-awesome/css/all.min.css">
  <link rel="stylesheet" href="/codeblog/lib/pace/pace-theme-minimal.min.css">
  <script src="/codeblog/lib/pace/pace.min.js"></script>

<script id="hexo-configurations">
    var NexT = window.NexT || {};
    var CONFIG = {"hostname":"skycoin.gitee.io","root":"/codeblog/","scheme":"Gemini","version":"7.8.0","exturl":false,"sidebar":{"position":"left","display":"post","padding":18,"offset":12,"onmobile":false},"copycode":{"enable":false,"show_result":false,"style":null},"back2top":{"enable":true,"sidebar":true,"scrollpercent":true},"bookmark":{"enable":false,"color":"#222","save":"auto"},"fancybox":false,"mediumzoom":false,"lazyload":false,"pangu":false,"comments":{"style":"tabs","active":"valine","storage":true,"lazyload":false,"nav":null,"activeClass":"valine"},"algolia":{"hits":{"per_page":10},"labels":{"input_placeholder":"Search for Posts","hits_empty":"We didn't find any results for the search: ${query}","hits_stats":"${hits} results found in ${time} ms"}},"localsearch":{"enable":true,"trigger":"auto","top_n_per_article":1,"unescape":false,"preload":false},"motion":{"enable":true,"async":false,"transition":{"post_block":"fadeIn","post_header":"slideDownIn","post_body":"slideDownIn","coll_header":"slideLeftIn","sidebar":"slideUpIn"}},"path":"search.xml"};
  </script>

  <meta name="description" content="[toc] 1.通过变量使用原始值与引用值1.1 原始值与引用值ES 变量可以包含两种不同类型的数据：原始值和引用值 原始值：最简单的数据 引用值：由多个值构成的对象 Tips:在把一个值赋给变量时，js 引擎必须确定这个值时原始值还是引用值 1234567891011&#x2F;&#x2F; 原始值undefinednullbooleannumberstringsymbol&#x2F;&#x2F; 引用值时保存在内存中的对象js不允许">
<meta property="og:type" content="article">
<meta property="og:title" content="Javascript基础（三）：javascript的变量、作用域与内存">
<meta property="og:url" content="http://skycoin.gitee.io/codeblog/2021/02/27/%E5%8F%98%E9%87%8F%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%92%8C%E5%86%85%E5%AD%98/index.html">
<meta property="og:site_name" content="若水的前端博客">
<meta property="og:description" content="[toc] 1.通过变量使用原始值与引用值1.1 原始值与引用值ES 变量可以包含两种不同类型的数据：原始值和引用值 原始值：最简单的数据 引用值：由多个值构成的对象 Tips:在把一个值赋给变量时，js 引擎必须确定这个值时原始值还是引用值 1234567891011&#x2F;&#x2F; 原始值undefinednullbooleannumberstringsymbol&#x2F;&#x2F; 引用值时保存在内存中的对象js不允许">
<meta property="og:locale" content="zh_CN">
<meta property="article:published_time" content="2021-02-27T02:14:52.000Z">
<meta property="article:modified_time" content="2021-04-15T01:24:35.085Z">
<meta property="article:author" content="若说">
<meta property="article:tag" content="前端">
<meta name="twitter:card" content="summary">

<link rel="canonical" href="http://skycoin.gitee.io/codeblog/2021/02/27/%E5%8F%98%E9%87%8F%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%92%8C%E5%86%85%E5%AD%98/">


<script id="page-configurations">
  // https://hexo.io/docs/variables.html
  CONFIG.page = {
    sidebar: "",
    isHome : false,
    isPost : true,
    lang   : 'zh-CN'
  };
</script>

  <title>Javascript基础（三）：javascript的变量、作用域与内存 | 若水的前端博客</title>
  






  <noscript>
  <style>
  .use-motion .brand,
  .use-motion .menu-item,
  .sidebar-inner,
  .use-motion .post-block,
  .use-motion .pagination,
  .use-motion .comments,
  .use-motion .post-header,
  .use-motion .post-body,
  .use-motion .collection-header { opacity: initial; }

  .use-motion .site-title,
  .use-motion .site-subtitle {
    opacity: initial;
    top: initial;
  }

  .use-motion .logo-line-before i { left: initial; }
  .use-motion .logo-line-after i { right: initial; }
  </style>
</noscript>

<link rel="alternate" href="/codeblog/atom.xml" title="若水的前端博客" type="application/atom+xml">
</head>

<body itemscope itemtype="http://schema.org/WebPage">
  <div class="container use-motion">
    <div class="headband"></div>
    <a target="_blank" rel="noopener" href="https://your-url" class="github-corner" aria-label="View source on GitHub"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:#70B7FD; color:#fff; position: absolute; top: 0; border: 0; right: 0;" aria-hidden="true"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>

    <header class="header" itemscope itemtype="http://schema.org/WPHeader">
      <div class="header-inner"><div class="site-brand-container">
  <div class="site-nav-toggle">
    <div class="toggle" aria-label="切换导航栏">
      <span class="toggle-line toggle-line-first"></span>
      <span class="toggle-line toggle-line-middle"></span>
      <span class="toggle-line toggle-line-last"></span>
    </div>
  </div>

  <div class="site-meta">

    <a href="/codeblog/" class="brand" rel="start">
      <span class="logo-line-before"><i></i></span>
      <h1 class="site-title">若水的前端博客</h1>
      <span class="logo-line-after"><i></i></span>
    </a>
      <p class="site-subtitle" itemprop="description">最优秀的模式往往是最简单的东西</p>
  </div>

  <div class="site-nav-right">
    <div class="toggle popup-trigger">
        <i class="fa fa-search fa-fw fa-lg"></i>
    </div>
  </div>
</div>




<nav class="site-nav">
  <ul id="menu" class="main-menu menu">
        <li class="menu-item menu-item-home">

    <a href="/codeblog/" rel="section"><i class="fa fa-home fa-fw"></i>首页</a>

  </li>
        <li class="menu-item menu-item-tags">

    <a href="/codeblog/tags/" rel="section"><i class="fa fa-tags fa-fw"></i>标签</a>

  </li>
        <li class="menu-item menu-item-categories">

    <a href="/codeblog/categories/" rel="section"><i class="fa fa-th fa-fw"></i>分类</a>

  </li>
        <li class="menu-item menu-item-archives">

    <a href="/codeblog/archives/" rel="section"><i class="fa fa-archive fa-fw"></i>归档</a>

  </li>
      <li class="menu-item menu-item-search">
        <a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索
        </a>
      </li>
  </ul>
</nav>



  <div class="search-pop-overlay">
    <div class="popup search-popup">
        <div class="search-header">
  <span class="search-icon">
    <i class="fa fa-search"></i>
  </span>
  <div class="search-input-container">
    <input autocomplete="off" autocapitalize="off"
           placeholder="搜索..." spellcheck="false"
           type="search" class="search-input">
  </div>
  <span class="popup-btn-close">
    <i class="fa fa-times-circle"></i>
  </span>
</div>
<div id="search-result">
  <div id="no-result">
    <i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i>
  </div>
</div>

    </div>
  </div>

</div>
    </header>

    


    <main class="main">
      <div class="main-inner">
        <div class="content-wrap">
          

          <div class="content post posts-expand">
            

    
  
  
  <article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN">
    <link itemprop="mainEntityOfPage" href="http://skycoin.gitee.io/codeblog/2021/02/27/%E5%8F%98%E9%87%8F%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%92%8C%E5%86%85%E5%AD%98/">

    <span hidden itemprop="author" itemscope itemtype="http://schema.org/Person">
      <meta itemprop="image" content="https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=4083356047,3551369852&fm=26&gp=0.jpg">
      <meta itemprop="name" content="若说">
      <meta itemprop="description" content="立志完成前端知识体系的架构">
    </span>

    <span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization">
      <meta itemprop="name" content="若水的前端博客">
    </span>
      <header class="post-header">
      
        <i class="fa fa-thumb-tack"></i>
        <font color=7D26CD>置顶</font>
      
        <h1 class="post-title" itemprop="name headline">
          Javascript基础（三）：javascript的变量、作用域与内存
        </h1>

        <div class="post-meta">
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-calendar"></i>
              </span>
              <span class="post-meta-item-text">发表于</span>

              <time title="创建时间：2021-02-27 10:14:52" itemprop="dateCreated datePublished" datetime="2021-02-27T10:14:52+08:00">2021-02-27</time>
            </span>
              <span class="post-meta-item">
                <span class="post-meta-item-icon">
                  <i class="far fa-calendar-check"></i>
                </span>
                <span class="post-meta-item-text">更新于</span>
                <time title="修改时间：2021-04-15 09:24:35" itemprop="dateModified" datetime="2021-04-15T09:24:35+08:00">2021-04-15</time>
              </span>
            <span class="post-meta-item">
              <span class="post-meta-item-icon">
                <i class="far fa-folder"></i>
              </span>
              <span class="post-meta-item-text">分类于</span>
                <span itemprop="about" itemscope itemtype="http://schema.org/Thing">
                  <a href="/codeblog/categories/%E5%89%8D%E7%AB%AF%E6%A1%86%E6%9E%B6/" itemprop="url" rel="index"><span itemprop="name">前端框架</span></a>
                </span>
            </span>

          
            <span id="/codeblog/2021/02/27/%E5%8F%98%E9%87%8F%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%92%8C%E5%86%85%E5%AD%98/" class="post-meta-item leancloud_visitors" data-flag-title="Javascript基础（三）：javascript的变量、作用域与内存" title="阅读次数">
              <span class="post-meta-item-icon">
                <i class="fa fa-eye"></i>
              </span>
              <span class="post-meta-item-text">阅读次数：</span>
              <span class="leancloud-visitors-count"></span>
            </span>
  
  <span class="post-meta-item">
    
      <span class="post-meta-item-icon">
        <i class="far fa-comment"></i>
      </span>
      <span class="post-meta-item-text">Valine：</span>
    
    <a title="valine" href="/codeblog/2021/02/27/%E5%8F%98%E9%87%8F%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%92%8C%E5%86%85%E5%AD%98/#valine-comments" itemprop="discussionUrl">
      <span class="post-comments-count valine-comment-count" data-xid="/codeblog/2021/02/27/%E5%8F%98%E9%87%8F%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%92%8C%E5%86%85%E5%AD%98/" itemprop="commentCount"></span>
    </a>
  </span>
  
  

        </div>
      </header>

    
    
    
    <div class="post-body" itemprop="articleBody">

      
        <p>[toc]</p>
<h2 id="1-通过变量使用原始值与引用值"><a href="#1-通过变量使用原始值与引用值" class="headerlink" title="1.通过变量使用原始值与引用值"></a>1.通过变量使用原始值与引用值</h2><h3 id="1-1-原始值与引用值"><a href="#1-1-原始值与引用值" class="headerlink" title="1.1 原始值与引用值"></a>1.1 原始值与引用值</h3><p>ES 变量可以包含两种不同类型的数据：原始值和引用值</p>
<p>原始值：最简单的数据</p>
<p>引用值：由多个值构成的对象</p>
<p>Tips:在把一个值赋给变量时，js 引擎必须确定这个值时原始值还是引用值</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">// 原始值</span></span><br><span class="line"><span class="literal">undefined</span></span><br><span class="line"><span class="literal">null</span></span><br><span class="line">boolean</span><br><span class="line">number</span><br><span class="line">string</span><br><span class="line">symbol</span><br><span class="line"></span><br><span class="line"><span class="comment">// 引用值时保存在内存中的对象</span></span><br><span class="line">js不允许直接访问内存位置，因此不能直接操作对象所在的内存空间</span><br><span class="line">在操作对象时，实际上的操作时对该对象的引用，而非实际的对象本身</span><br></pre></td></tr></table></figure>

<p>补充</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line">js中的内存分为堆内存和栈内存</span><br><span class="line">堆内存：存储引用类型值</span><br><span class="line">栈内存：提供js代码执行的环境和存储基本类型</span><br><span class="line"></span><br><span class="line">堆内存释放：让所有堆内存空间地址的变量赋值为<span class="literal">null</span>即可</span><br><span class="line">栈内存释放：</span><br><span class="line"><span class="number">1</span>。函数执行完成，当前形成的栈内存中，某些内容被栈内存以外的变量占用了</span><br><span class="line"><span class="number">2.</span>全局栈内存只有在页面关闭的时候才会释放掉</span><br></pre></td></tr></table></figure>

<h3 id="1-1-1-动态属性"><a href="#1-1-1-动态属性" class="headerlink" title="1.1.1 动态属性"></a>1.1.1 动态属性</h3><p>原始值和引用值定义方式类似，都是创建一个变量，然后赋值。不过在变量保存这个值之后，可以对这个值的操作不同。</p>
<ul>
<li>对于引用值来说，可以随时添加、修改和删除其属性的方法</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> person = <span class="keyword">new</span> <span class="built_in">Object</span>();</span><br><span class="line">person.name = <span class="string">&quot;nick&quot;</span>; <span class="comment">// 给对象添加一个name属性</span></span><br></pre></td></tr></table></figure>

<ul>
<li>原始值不能有属性</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> name = <span class="string">&quot;nick&quot;</span>;</span><br><span class="line">name.age = <span class="number">27</span>; <span class="comment">// undefined</span></span><br><span class="line"><span class="comment">// 原始类型的初始化可以使用原始字面量的形式。</span></span><br></pre></td></tr></table></figure>

<h3 id="1-1-2-复制值"><a href="#1-1-2-复制值" class="headerlink" title="1.1.2 复制值"></a>1.1.2 复制值</h3><p>除了存储方式不同，原始值和引用值在通过变量赋值时也有所不同。</p>
<ul>
<li>在通过变量把一个原始值赋值到另一个变量时，原始值也会被复制到新变量的位置</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> num1 = <span class="number">5</span>;</span><br><span class="line"><span class="keyword">let</span> num2 = num1;</span><br><span class="line"><span class="comment">// 这两个变量可以单独使用，互不干扰</span></span><br></pre></td></tr></table></figure>

<ul>
<li>在把引用值从一个变量赋给另一个变量时，存储在变量中的值也会被复制到新变量所在的位置，区别在于，这里复制的值实际上是一个指针，它指向存储在堆内存中的对象。操作完成后，两个变量实际上指向同一个对象，因此一个对象变化另一个对象也会变化</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> obj1 = <span class="keyword">new</span> <span class="built_in">Object</span>();</span><br><span class="line">obj1.name = <span class="string">&quot;nick&quot;</span>;</span><br><span class="line"><span class="keyword">let</span> obj2 = obj1;</span><br><span class="line">obj2.name; <span class="comment">// nick</span></span><br></pre></td></tr></table></figure>

<h3 id="1-1-3-传递参数"><a href="#1-1-3-传递参数" class="headerlink" title="1.1.3 传递参数"></a>1.1.3 传递参数</h3><p>es 中所有的函数都是按值传递的。函数外的值会被复制到函数内部的参数中，就像从一个变量复制到另一个变量一样。</p>
<p>按值传递参数时，值会被复制到一个局部变量（即一个命名参数，或者用 es 的话说，就像 arguments 对象中的一个槽位）</p>
<ul>
<li>传递原始值的时候</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">addTen</span>(<span class="params">num</span>) </span>&#123;</span><br><span class="line">  num += <span class="number">10</span>;</span><br><span class="line">  <span class="keyword">return</span> num;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> count = <span class="number">20</span>;</span><br><span class="line"><span class="keyword">let</span> result = addTen(count); <span class="comment">// count = 20   result = 30 没有变化</span></span><br></pre></td></tr></table></figure>

<ul>
<li>传递引用值的时候</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">setName</span>(<span class="params">obj</span>) </span>&#123;</span><br><span class="line">  obj.name = <span class="string">&quot;nick&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> person = <span class="keyword">new</span> <span class="built_in">Object</span>();</span><br><span class="line">setName(person);</span><br><span class="line"><span class="built_in">console</span>.log(person.name); <span class="comment">// nick</span></span><br><span class="line"><span class="comment">// 在函数内部，obj和perosn都指向同一个对象。所以即使对象时按值传进函数的，obj也会通过引用访问对象。当函数内给obj设置了name属性时，函数外部的对象保存在全局作用的堆内存上</span></span><br></pre></td></tr></table></figure>

<h3 id="1-1-4-确定类型"><a href="#1-1-4-确定类型" class="headerlink" title="1.1.4 确定类型"></a>1.1.4 确定类型</h3><ul>
<li>原始值</li>
</ul>
<p>typeof 操作符最适合来判断一个变量是否为原始类型，如果值是对象或者 null,那么 typeof 返回 object，如果想知道是什么类型的对象就需要看引用值</p>
<ul>
<li>引用值</li>
</ul>
<p>使用 instanceof 操作符</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line">result = variable instaceof <span class="title">constructor</span></span><br><span class="line"></span><br><span class="line">// 如果变量给定引用类型（由其原型链决定）的实例，则<span class="title">instanceof</span>操作符返回<span class="title">true</span></span><br><span class="line"><span class="title">person</span> <span class="title">instanceof</span> <span class="title">Object</span>   // 变量<span class="title">person</span>是<span class="title">object</span>吗</span><br><span class="line"><span class="title">colors</span> <span class="title">instanceof</span> <span class="title">Array</span>    // 变量<span class="title">colors</span>是<span class="title">Array</span>吗</span><br><span class="line"><span class="title">pattern</span> <span class="title">instanceof</span> <span class="title">RegExp</span>  // 变量<span class="title">pattern</span>是<span class="title">RegExp</span></span><br></pre></td></tr></table></figure>

<p>所有引用值都是 Object 的实例，因此通过 instanceof 操作符检测任何引用值和 object 构造函数都会返回 true。</p>
<h4 id="1-1-5-js-代码运行的过程"><a href="#1-1-5-js-代码运行的过程" class="headerlink" title="1.1.5 js 代码运行的过程"></a>1.1.5 js 代码运行的过程</h4><p>Js 是动态语言，任何一段代码在执行之前都需要编译，它跟传统的语言不同，它不是提前编译的，编译的结果也不能在分布式系统中进行移植</p>
<p>但是 js 引擎进行编译的步骤和传统的编译语言非常类似</p>
<p><strong>传统的编译</strong></p>
<ul>
<li>分词/词法分析</li>
</ul>
<p>这个过程将由字符串组成的字符串分解成有意义的代码块，这些代码块称为词法单元</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line">e.g  <span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line">通过被解析成  <span class="keyword">var</span> 、a、=、<span class="number">2</span>、;</span><br><span class="line">空格是否被当做此法单元，取决于空格在这门语言中是否具有意义</span><br></pre></td></tr></table></figure>

<ul>
<li>解析/语法分析</li>
</ul>
<p>这个过程是将词法单元流（数组）转换成一个由元素逐级嵌套所组成的代表了程序语法结构的树（抽象语法树）</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">2</span>;</span><br><span class="line">通过特定方法将<span class="keyword">var</span>=<span class="number">2</span>的AST转化为一组机器指令，用来创建一个叫做a的变量，并将一个值存储到a中</span><br></pre></td></tr></table></figure>

<p><strong>js 编译</strong></p>
<p>js 的编译步骤和传统还是非常相似的，只是某些环节比较复杂，这里说一下预编译，其他三个步骤同传统的编译</p>
<ul>
<li>分词/词法分析</li>
<li>解析/语法分析</li>
<li>预编译</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">a</span>(<span class="params">b</span>) </span>&#123;</span><br><span class="line">  alert(b);</span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">b</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    alert(b);</span><br><span class="line">  &#125;</span><br><span class="line">  b();</span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line">a(<span class="number">1</span>);</span><br></pre></td></tr></table></figure>

<p>答案先不说，现在看具体的预编译过程</p>
<p>1.预编译–全局</p>
<ul>
<li><p>创建 Global Object 对象（GO)</p>
<p>2.查找变量声明</p>
</li>
<li><p>如果 Go 上还没有该属性，则添加属性，值为 undefined</p>
</li>
<li><p>如果 Go 上已经有该属性，则不做任何处理</p>
<p>3.查找函数声明</p>
</li>
<li><p>如果 Go 上还没有 foo 属性，则把函数赋值给 foo 属性</p>
</li>
<li><p>如果 Go 上已经存在 foo 属性，则直接覆盖</p>
</li>
</ul>
<p>预编译–函数</p>
<ul>
<li>函数运行前的一瞬间，生成 Activation Object，简称 AO</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">a</span>(<span class="params">b</span>) </span>&#123;</span><br><span class="line">  alert(b);</span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">b</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    alert(b);</span><br><span class="line">  &#125;</span><br><span class="line">  b();</span><br><span class="line">&#125;</span><br><span class="line">a(<span class="number">1</span>);</span><br><span class="line"></span><br><span class="line"><span class="comment">// 分析如下</span></span><br><span class="line"><span class="comment">/*</span></span><br><span class="line"><span class="comment"> * 1. 创建GO对象（包含JS全局对象的内置对象Math、String、Date、etc）</span></span><br><span class="line"><span class="comment"> * 2. 查找变量声明，没有</span></span><br><span class="line"><span class="comment"> * 3. 查找函数声明，定义函数a, GO = &#123;a: function () &#123;&#125;&#125;</span></span><br><span class="line"><span class="comment"> * 4. 执行a(1)</span></span><br><span class="line"><span class="comment"> * // 以下为函数a运行前的编译</span></span><br><span class="line"><span class="comment"> * 5. 创建活动对象AO  AO=&#123;this, arguments&#125;</span></span><br><span class="line"><span class="comment"> * 6. 分析形参 AO = &#123;this, arguments, b: undefined&#125;</span></span><br><span class="line"><span class="comment"> * 7. 接收实参 AO = &#123;this, arguments, b: 1&#125;</span></span><br><span class="line"><span class="comment"> * 8. 分析变量声明 AO = &#123;this, arguments, b: 1&#125;</span></span><br><span class="line"><span class="comment"> * 9. 分析函数声明 </span></span><br><span class="line"><span class="comment">     AO = &#123;</span></span><br><span class="line"><span class="comment">         this</span></span><br><span class="line"><span class="comment">         argunments,</span></span><br><span class="line"><span class="comment">         b: function () &#123;&#125;</span></span><br><span class="line"><span class="comment">     &#125;</span></span><br><span class="line"><span class="comment"> </span></span><br><span class="line"><span class="comment"> * // 执行</span></span><br><span class="line"><span class="comment"> * alert(b)  // function () &#123; ... &#125;</span></span><br><span class="line"><span class="comment"> * b()   // function () &#123; ... &#125;</span></span><br><span class="line"><span class="comment"> */</span></span><br></pre></td></tr></table></figure>

<h3 id="1-2-执行上下文与作用域"><a href="#1-2-执行上下文与作用域" class="headerlink" title="1.2 执行上下文与作用域"></a>1.2 执行上下文与作用域</h3><p>变量或函数的上下文决定了它们可以访问哪些数据，以及他们的行为。每个上下文都有一个关联的变量对象，而这个上下文中定义的所有变量和函数都存在于这个对象上</p>
<p>全局上下文是最外层的上下文。根据 es 实现的宿主环境，表示全局上下文的对象可能不一样。在浏览器中，全局上下文就是我们所说的 window 对象，因此所有通过 var 定义的全局变量和函数都会成为 window 对象的属性和方法。使用 let 和 const 的顶级声明不会定义在全局上下文中，但在作用域解析上效果是一样的。</p>
<p>每个函数调用都有自己的上下文。当代码执行流进入函数时，函数的上下文被推到一个上下文栈上。在函数执行完之后，上下文栈会弹出该函数上下文，将控制权返还给之前的执行上下文。es 程序的执行流就是通过这个上下文栈进行控制的</p>
<p>上下文中的代码在执行的时候，会创建变量对象的一个作用域链。这个作用域链决定了各级上下文中的代码在访问变量和函数时的顺序。代码正在执行的上下文的变量对象始终位于作用域链的最前端。如果上下是函数，则其活动对象用做变量对象。活动对象最初只有一个定义变量：arguments。作用域链中的下一个变量对象来自包含上下文，在下一个对象来自在下一个包含上下文，全局的上下文的变量对象始终是作用域链的最后一个变量对象</p>
<p>代码执行时的标识符解析时通过沿作用域链逐级搜索表示名称完成的，搜索过程始终从作用域链的最前端开始，然后逐级往后，直到找到标识符</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> color = <span class="string">&quot;blue&quot;</span>;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">changeColor</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">if</span> (color === <span class="string">&quot;blue&quot;</span>) &#123;</span><br><span class="line">    color = <span class="string">&quot;red&quot;</span>;</span><br><span class="line">  &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">    color = <span class="string">&quot;blue&quot;</span>;</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line">changeColor();</span><br><span class="line"><span class="comment">//函数changeColor()的作用域链包含两个对象：一个是它自己的变量对象（就是定义arguments对象那个)，另一个就是全局上下文的变量对象。这个函数内部之所以能够访问变量color，就是因为可以在作用域链中找到他</span></span><br></pre></td></tr></table></figure>

<p>此外，局部作用域中定义的变量可用于在局部上下文中替换全局变量</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> color = <span class="string">&quot;blue&quot;</span>;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">changeColor</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> anotherColor = <span class="string">&quot;red&quot;</span>;</span><br><span class="line">  <span class="function"><span class="keyword">function</span> <span class="title">swapColors</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">let</span> tempColor = antherColor;</span><br><span class="line">    antherColor = color;</span><br><span class="line">    color = tempColor;</span><br><span class="line">    <span class="comment">//这里可以访问color、anotherColor和tempColor</span></span><br><span class="line">  &#125;</span><br><span class="line">  <span class="comment">// 这里可以访问color和anotherColor，但访问不到tempColor</span></span><br><span class="line">  swapColors();</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// 这里只能访问color</span></span><br><span class="line"></span><br><span class="line">changeColor();</span><br><span class="line"></span><br><span class="line"><span class="comment">// 这个例子告诉我们，内部上下文可以通过作用域链访问外部上下文中的一切，但外部的上下文，无法访问内部上下文的任何东西。</span></span><br></pre></td></tr></table></figure>

<p>1.2.1 详解：执行上下文与可执行代码</p>
<p>可执行代码的类型</p>
<ul>
<li>全局代码：例如加载外部的 js 文件或者本地标签内的代码。全局代码不包过 function 体内的代码</li>
<li>函数代码：function 代码</li>
<li>eval 代码：eval()函数计算某个字符串，并执行其中的 js 代码，比如：<code>eval(&quot;alert(&#39;hello woeld&#39;)&quot;)</code></li>
</ul>
<p>当 js 引擎遇到这三种类型的代码时候，都是会进行一些准备工作，这些准备工作，专业的说法就叫执行上下文。</p>
<p>简单来说，执行上下文就是评估和执行 js 代码的环境的抽象概念。每当 js 代码在运行的时候，他都是在执行上下文运行。执行上下文可以理解为当前代码的执行环境，他会形成一个作用域。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">js代码的整个执行过程，分为两个阶段，代码编译阶段与代码执行阶段。编译阶段由编译器完成，将代码编译成可执行代码，这个阶段作用域规则会确定。执行阶段由引擎完成，主要任务是执行课执行代码，执行上下文在这个阶段创建</span><br></pre></td></tr></table></figure>

<p>1.2.1 那么 js 引擎在遇到可执行代码的时候，它究竟会做哪些准备工作呢</p>
<ul>
<li><p>全局执行上下文：创建一个全局的 window 对象（浏览器的情况下），并且设置 this 的值等于这个全局对象。一个程序只有一个全局执行上下文</p>
</li>
<li><p>函数执行上下文：每当一个函数被调用时，都会为该函数创建一个新的上下文，每个函数都有自己的执行上下文，不过是在函数被调用时调用时创建的，函数的上下文可以有人一个，每当一个新的执行上下文被创建，他会按定义的顺序执行一系列步骤</p>
<p>1.2.2 执行上下文栈</p>
</li>
</ul>
<p>在 js 程序中，必定会产生多个执行上下文，js 引擎会以栈的方式来处理它们，它就是执行上下文栈</p>
<h2 id="2-理解执行上下文"><a href="#2-理解执行上下文" class="headerlink" title="2.理解执行上下文"></a>2.理解执行上下文</h2><p>1.2.3 执行上下文的生命周期</p>
<p>创建阶段</p>
<ul>
<li>生成变量对象</li>
<li>建立作用域链</li>
<li>确定 this 指向</li>
</ul>
<p>执行阶段</p>
<ul>
<li>变量赋值</li>
<li>函数引用</li>
<li>执行其他代码</li>
</ul>
<h3 id="1-3-作用域链增强"><a href="#1-3-作用域链增强" class="headerlink" title="1.3 作用域链增强"></a>1.3 作用域链增强</h3><p>作用域链是什么？</p>
<p>包含了函数被创建的作用域中对象的集合就是 js 中的作用域链。作用域链决定了哪些数据能被函数访问。当一个函数创建后，他的作用域链会被创建此函数的作用域中可访问的数据对象填充</p>
<p>某些语句会导致在作用域前端临时添加一个上下文，这个上下文在代码执行后会被删除</p>
<p>Try/catch 语句中的 catch 块</p>
<p>with 语句</p>
<p>这两种情况下，都会在作用域链前添加一个变量对象。对 with 来说，会向作用域链前添加指定的对象；对 catch 语句而言，则会创建一个新的变量对象，这个变量对象会包含要抛出错误对象的声明</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">buildUrl</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> qs = <span class="string">&quot;? debug=true&quot;</span>;</span><br><span class="line">  <span class="keyword">with</span> (location) &#123;</span><br><span class="line">    <span class="keyword">let</span> url = href + qs;</span><br><span class="line">  &#125;</span><br><span class="line">  <span class="keyword">return</span> url;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// with语句将location对象作为上下文，因此location会被添加到作用域链前端。当with语句中的代码引用变量href时，实际上引用的事location.href，也就是自己的变量对象属性，在引用qs时，引用的则是定义在buildUrl（）中的那个变量，他定义在函数上下文的变量对象上，</span></span><br></pre></td></tr></table></figure>

<p>1.3.1 变量声明</p>
<p>es6 之后，js 的变量声明经历了翻天覆地的变化，不仅增加了 let 和 const 两个关键字，而且还让这两个关键字压倒性地超越 var 称为首选</p>
<ul>
<li>使用 var 的函数作用域声明</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line">在使用<span class="keyword">var</span>声明变量时，变量会被自动添加到最接近的上下文。在函数中最接近的上下文就是函数的局部上下文。在<span class="keyword">with</span>语句中最接近的上下文也是函数上下文。如果变量未经声明就被初始化，那么它会自动被添加到全局上下文</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">add</span>(<span class="params">num1,num2</span>)</span>&#123;</span><br><span class="line">  <span class="keyword">var</span> sum = sum1 + sum2;</span><br><span class="line">  <span class="keyword">return</span> sum;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> result = add(<span class="number">10</span>,<span class="number">20</span>)</span><br><span class="line"><span class="built_in">console</span>.log(sum) <span class="comment">// VM1526:1 Uncaught ReferenceError: sum is not defined</span></span><br><span class="line"><span class="comment">// add()是定义了一个局部变量sum，保存加法操作的结果。这个值作为函数的值返回，但变量sum在函数外部是访问不到的，如果省略上面例子中的关键字var,那么sum在ad（）被调用之后就可以变成可以访问的</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">add</span>(<span class="params">num1,num2</span>)</span>&#123;</span><br><span class="line">  sum = sum1 + sum2</span><br><span class="line">  <span class="keyword">return</span> sum;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> result = add(<span class="number">10</span>,<span class="number">20</span>)</span><br><span class="line"><span class="built_in">console</span>.log(sum) <span class="comment">// 30</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 这一次变量sum被用加法操作的结果初始化并没有使用var声明，在调用add()之后，sum被添加到全局上下文，在函数退出之后仍然存在</span></span><br><span class="line">tips:未经声明而初始化的变量是js常见错误，在严格模式会报错</span><br></pre></td></tr></table></figure>

<p>var 声明会被拿到函数或全局作用域的顶部，位于作用域中所有代码之前，这个现象叫做提升。提升让同一作用域的代码不必考虑变量是否已经声明就可以直接使用。可在实践中，提升也会导致合法却奇怪的现象，即在变量声明之前使用变量</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> name = <span class="string">&#x27;jake&#x27;</span>;</span><br><span class="line">===</span><br><span class="line">name = <span class="string">&#x27;jke&#x27;</span></span><br><span class="line"><span class="keyword">var</span> name;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">fn1</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  <span class="keyword">var</span> name = <span class="string">&#x27;jake&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">fn2</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  <span class="keyword">var</span> name;</span><br><span class="line">  name = <span class="string">&#x27;jake&#x27;</span></span><br><span class="line">&#125;</span><br><span class="line"></span><br><span class="line"><span class="comment">// 通过声明之前打印变量，可以验证变量会被提升。声明意味着会输出undefined而不是Reference Error</span></span><br></pre></td></tr></table></figure>

<ul>
<li>使用 let 的块级作用域声明</li>
</ul>
<p>与 var 类似，但它的作用域是块级的。块级作用域由最近的一堆花括号界定。简单说，if 块、while 块、function 块甚至是单独的块也是 let 声明变量的作用域</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">if</span> (<span class="literal">true</span>) &#123;</span><br><span class="line">  <span class="keyword">let</span> a;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">console</span>.log(a); <span class="comment">// a 没有定义</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 这不是对象字面量，而是一个独立的块，js解释器会根据其中内容识别出来</span></span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">let</span> d;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">console</span>.log(d); <span class="comment">// d没有定义</span></span><br></pre></td></tr></table></figure>

<p>与 var 另一个不同点是：重复的 var 会被忽略，而重复额 let 会抛出 SyntaxError</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a;</span><br><span class="line"><span class="keyword">var</span> a;</span><br><span class="line">&#123;</span><br><span class="line">  <span class="keyword">let</span> b;</span><br><span class="line">  <span class="keyword">let</span> b; <span class="comment">// error</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>let 的行为非常适合在循环中声明迭代变量。使用 var 声明的迭代变量会泄漏到循环外部</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">for</span> (<span class="keyword">var</span> i = <span class="number">0</span>; i &lt; <span class="number">10</span>; i++) &#123;&#125;</span><br><span class="line"><span class="built_in">console</span>.log(i); <span class="comment">// 10</span></span><br><span class="line"></span><br><span class="line"><span class="keyword">for</span> (<span class="keyword">let</span> i = <span class="number">0</span>; i &lt; <span class="number">0</span>; i++) &#123;&#125;</span><br><span class="line"><span class="built_in">console</span>.log(i); <span class="comment">// ReferenceError</span></span><br></pre></td></tr></table></figure>

<p>严格来讲，let 在 js 运行的时候也会提升，但是由于暂时性死区的缘故，实际上不能再声明之前使用 let 变量</p>
<ul>
<li>const 的常量变量</li>
</ul>
<p>使用 const 声明的变量必须同时初始化未某个值。一经声明，在其生命周期的任何时候不能重新赋予新值</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> a; <span class="comment">// syntaxError 常量声明时没有初始化</span></span><br><span class="line"><span class="keyword">const</span> b = <span class="number">3</span>;</span><br><span class="line"><span class="built_in">console</span>.log(b); <span class="comment">// 3</span></span><br><span class="line">b = <span class="number">4</span>; <span class="comment">// typeError</span></span><br></pre></td></tr></table></figure>

<p>Const 声明只应用到顶级原语或者对象。简单来说就是赋值为对象的 const 变量不能在被重新赋值为其他引用值，但对象的键不受控制</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> o1 = &#123;&#125;;</span><br><span class="line">o1 = &#123;&#125;; <span class="comment">// typeError</span></span><br><span class="line"><span class="keyword">const</span> o2 = &#123;&#125;;</span><br><span class="line">o2.name = <span class="string">&quot;jake&quot;</span>;</span><br></pre></td></tr></table></figure>

<p>如果想让整个对象都不能进行修改，可以使用 Object.freeze(),这样再给属性赋值时虽然不会报错，但会静默失败</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">const</span> o3 = <span class="built_in">Object</span>.freeze(&#123;&#125;);</span><br><span class="line">o3.name = <span class="string">&quot;jake&quot;</span>;</span><br><span class="line"><span class="built_in">console</span>.log(o3.name); <span class="comment">// undefined</span></span><br></pre></td></tr></table></figure>

<ul>
<li>标识符查找</li>
</ul>
<p>当在特定上下文中为读取或写入而引用一个标识符时，必须通过搜素确定这个标识符表示什么。搜素开始与作用链前端，以给定的名称搜索对应的标识符。如果在局部上下文中找到该标识符，则搜索停止，变量确定。如果没有找到变量名，则继续沿作用域链搜索。这个过程一直持续至全局上下文的变量对象。如果仍然没有找到标识符，则其说明未声明</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> color = <span class="string">&quot;blue&quot;</span>;</span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getColor</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">return</span> color;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">console</span>.log(getColor()); <span class="comment">// blue</span></span><br><span class="line"><span class="comment">// 在这个例子中，调用函数getColor会引用变量color。为确定color的值会进行两步搜索，第一步，搜索getColor()变量对象，查找名为color的标识符，因为全局变量对象上有color的定义，所以搜索结束</span></span><br><span class="line"><span class="comment">// 如果引用局部变量会让搜索自动结束</span></span><br></pre></td></tr></table></figure>

<p>使用块级作用域声明并不会改变搜索流程，但可以给词法添加额外的层次</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> color = <span class="string">&#x27;blue&#x27;</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">getColor</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  <span class="keyword">let</span> color = <span class="string">&#x27;red&#x27;</span></span><br><span class="line">  &#123;</span><br><span class="line">    <span class="keyword">let</span> color = <span class="string">&#x27;green&#x27;</span></span><br><span class="line">    retur color</span><br><span class="line">  &#125;</span><br><span class="line">&#125;</span><br><span class="line"><span class="built_in">console</span>.log(getColor()) <span class="comment">// green</span></span><br><span class="line"><span class="comment">// getColor内部声明了一个名为color的局部变量。在调用这个函数的时候，变量会被声明。在执行到这个函数时，代码引用变量color.于是开始在局部上下文搜索这个标识符，结果找到了值为green的变量color。因为变量已经找到，搜索随即停止</span></span><br></pre></td></tr></table></figure>

<p>标识符查找不是没有代价，访问局部变量比访问全局变量要快，因为不用切换作用域</p>
<p><strong>补充</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">var</span> a = <span class="number">10</span></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">foo</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">  <span class="built_in">console</span>.log(a)</span><br><span class="line">  <span class="keyword">var</span> a = <span class="number">10</span></span><br><span class="line">  <span class="built_in">console</span>.log(a)</span><br><span class="line">&#125;</span><br><span class="line"><span class="literal">undefined</span> <span class="number">10</span></span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">f1</span>(<span class="params"></span>)</span>&#123;</span><br><span class="line">	<span class="keyword">var</span> x = <span class="number">10</span></span><br><span class="line">  ++x</span><br><span class="line">  <span class="built_in">console</span>.log(x)</span><br><span class="line">&#125;</span><br><span class="line"><span class="literal">undefined</span> <span class="number">11</span></span><br></pre></td></tr></table></figure>

<h3 id="1-4-js-编译过程-VO-AO"><a href="#1-4-js-编译过程-VO-AO" class="headerlink" title="1.4 js 编译过程 VO AO"></a>1.4 js 编译过程 VO AO</h3><p>编译过程</p>
<ul>
<li>发现有代码 -&gt; 调用一个函数</li>
<li>在执行这个 function 之前，创建一个执行上下文(execution context)，也可以叫执行环境。</li>
<li>进入创建阶段（VO 创建）<ul>
<li>初始化作用域链(scope chain)</li>
<li>创建变量函数(variable object / VO)</li>
<li>创建参数对象（arguments object，传进来的参数），检查上下文，初始化其名字和值，以及建立引用对象的拷贝</li>
<li>扫描上下文中的函数声明</li>
<li>为每一个扫描到的函数声明在 Vo 中创建一个属性，命名为函数的名字，指向了存储空间中的对应函数</li>
<li>如果函数名称已经存在了，这个引用指针将被重写为新的这一个</li>
<li>扫描上下文中的变量声明</li>
<li>为每一个扫描到的变量在 VO 中创建一个属性，命名为变量的名字，初始值为 undefined</li>
<li>如果变量名在内存中已经存在了，就跳过</li>
<li>决定上下文中 this 的指向</li>
</ul>
</li>
<li>执行阶段<ul>
<li>执行/解释上下文中的 function，为变量赋值</li>
<li>代码按行执行</li>
</ul>
</li>
</ul>
<p>他们相应的概念</p>
<p>Scope:变量/函数起作用的区域</p>
<p>Scope chain:保证对执行环境有权访问的所有变量和函数的有序访问。相当于 VO+[scope]我们可以将作用域定义为一套规则，用来管理引擎如何在当前作用域以及嵌套的子作用域中根据标识符名称进行变量的查找，作用域链是这套规则的具体实现</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">exection context = &#123;VO,<span class="built_in">this</span>,[scope]&#125;</span><br><span class="line"><span class="built_in">this</span>:函数/方法的拥有者</span><br></pre></td></tr></table></figure>

<p><a target="_blank" rel="noopener" href="https://zhuanlan.zhihu.com/p/200910488">https://zhuanlan.zhihu.com/p/200910488</a></p>
<h2 id="3-理解垃圾回收"><a href="#3-理解垃圾回收" class="headerlink" title="3.理解垃圾回收"></a>3.理解垃圾回收</h2><p>js 是使用垃圾回收的语言，也就是说执行环境负责在代码执行是管理内存。跟踪内存使用对开发来说是个大负担。js 通过自动内存管理实现内存分配和闲置资源回收。基本思路就是：确定哪个变量不在使用，然后释放它占用的内存。函数中的局部变量会在函数执行时存在，此时，栈内存分配空间以保存相应的值。在浏览器发展历史上，用到过两种主要的标记策略：标记清理和引用计数。</p>
<h3 id="3-1-1-标记清理"><a href="#3-1-1-标记清理" class="headerlink" title="3.1.1 标记清理"></a>3.1.1 标记清理</h3><p>js 最常用的垃圾回收策略</p>
<p>当变量进入上下文，比如在函数内部声明一个变量时，这个变量会被加上存在于上下文的标记。而在上下文中的变量，逻辑上讲，永远不应该释放它们的内存，因为只要有上下文中的代码在运行，就可能用到它们。</p>
<p>给变量添加标记的方式有很多种。</p>
<h3 id="3-1-2-引用计数"><a href="#3-1-2-引用计数" class="headerlink" title="3.1.2 引用计数"></a>3.1.2 引用计数</h3><p>思路就是对每个值都记录它被引用的次数。声明变量并给它赋一个引用值时，这个值的引用数为 1.如果同一个值又被赋给另一个变量，那么引用数加 1.如果保存对该值引用的变量被其他值给覆盖了，那么引用数减 1</p>
<p>问题：循环引用</p>
<p>对象 A 包含一个指向对象 B 的指针，而对象 B 中也包含一个指向 A 的指针</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">obj</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">var</span> objA = <span class="keyword">new</span> <span class="built_in">Object</span>();</span><br><span class="line">  <span class="keyword">var</span> objB = <span class="keyword">new</span> <span class="built_in">Object</span>();</span><br><span class="line">  objA.propertyA = objBl;</span><br><span class="line">  objB.property = objA;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure>

<p>解决</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">objA.propertyA = <span class="literal">null</span>;</span><br><span class="line">objB.property = <span class="literal">null</span>;</span><br></pre></td></tr></table></figure>

<h3 id="3-1-3-性能"><a href="#3-1-3-性能" class="headerlink" title="3.1.3 性能"></a>3.1.3 性能</h3><p>垃圾回收程序会周期性运行，如果内存中分配了很多变量，则可能造成性能损失，因此垃圾回收时间的调度很重要。垃圾回收会明显拖慢渲染的速度和帧速率。开发者不知道什么时候运行时会收集垃圾，因此最好的办法是在写代码时要做到，无论什么时候开始收集垃圾，都能让它尽快结束。</p>
<h3 id="3-1-4-内存管理"><a href="#3-1-4-内存管理" class="headerlink" title="3.1.4 内存管理"></a>3.1.4 内存管理</h3><p>在使用垃圾回收的编程环境中，开发者无需关心内存管理。将内存占用量保持在一个较小的值可以让页面性能更好。优化内存占用的最佳手段就是保证在执行代码时只保存必要的数据。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">createPerson</span>(<span class="params">name</span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> localPerson = <span class="keyword">new</span> <span class="built_in">Object</span>();</span><br><span class="line">  localPerson.name = name;</span><br><span class="line">  <span class="keyword">return</span> localPerson;</span><br><span class="line">&#125;</span><br><span class="line"><span class="keyword">let</span> globalPerson = createPerson(<span class="string">&quot;nick&quot;</span>);</span><br><span class="line">globalPerosn = <span class="literal">null</span>;</span><br></pre></td></tr></table></figure>

<p>在上面代码中，变量 globalPerson 保存着 createPerson 的调用值。在 createPerson 内部，localPerson 创建了一个对象并给他添加一个 name 属性。localPerson 在 createPerson()执行完成超出上下文后会自动解除引用，不需要显示处理。</p>
<ul>
<li>通过 const 和 let 声明提升性能</li>
</ul>
<p>es6 增加这两个关键字不仅有助于改善代码风格，而且同样有助于改进垃圾回收的过程。因为 const 和 let 都以块为作用域，所以相比于使用 var，使用这两个新关键字可能会更早地让垃圾回收程序介入，今早回收应该回收的内存。</p>
<ul>
<li>隐藏类和删除操作</li>
</ul>
<p>根据 js 所在的运行环境，运行期间 v8 会创建的对象与隐藏类关联起来，跟踪他们的属性特征。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">Article</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="built_in">this</span>.title = <span class="string">&quot;title&quot;</span>;</span><br><span class="line">  <span class="keyword">let</span> a1 = <span class="keyword">new</span> Article();</span><br><span class="line">  <span class="keyword">let</span> a2 = <span class="keyword">new</span> Article();</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//v8会在后台配置，让这两个类实例共享相同的隐藏类，因为这两个实例共享同一个构造函数和原型。</span></span><br></pre></td></tr></table></figure>

<ul>
<li>内存泄漏</li>
</ul>
<p>在内存有限的设备上，或者在函数会被调用多次的情况下，内存泄漏是大问题。</p>
<p>意外声明全局变量是最常见但也是最容易修复的。</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br></pre></td><td class="code"><pre><span class="line"><span class="function"><span class="keyword">function</span> <span class="title">setName</span>(<span class="params"></span>) </span>&#123;</span><br><span class="line">  name = <span class="string">&quot;jake&quot;</span>;</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">// == window.name 只要window本身不被清理就不会消失</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// 定时器也会导致内存泄漏</span></span><br><span class="line"><span class="keyword">let</span> name = <span class="string">&quot;jake&quot;</span>;</span><br><span class="line"><span class="built_in">setInterval</span>(<span class="function">() =&gt;</span> &#123;</span><br><span class="line">  <span class="built_in">console</span>.log(name);</span><br><span class="line">&#125;, <span class="number">100</span>);</span><br><span class="line"><span class="comment">//只要定时器一直运行，回调函数中引用的name就会一直占用内存</span></span><br><span class="line"></span><br><span class="line"><span class="comment">// js闭包也很容易造成泄漏</span></span><br><span class="line"><span class="keyword">let</span> outer = <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">  <span class="keyword">let</span> name = <span class="string">&quot;jake&quot;</span>;</span><br><span class="line">  <span class="keyword">return</span> <span class="function"><span class="keyword">function</span> (<span class="params"></span>) </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> name;</span><br><span class="line">  &#125;;</span><br><span class="line">&#125;;</span><br><span class="line"><span class="comment">// 以上代码执行后创建一个内部闭包，只要返回的函数存在就不能清理name，因为闭包一直在引用着它</span></span><br></pre></td></tr></table></figure>

<ul>
<li>静态分配与对象池</li>
</ul>
<p>为了提升 js 性能，最后要考虑的就是一步步压榨浏览器。一个关键问题就是如何减少浏览器回收的次数。开发者无法直接控制什么时候开始收集垃圾，但可以进阶控制触发垃圾回收的条件。</p>
<p><strong>例</strong></p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="keyword">let</span> a = &#123; <span class="attr">n</span>: <span class="number">1</span> &#125;;</span><br><span class="line"><span class="keyword">let</span> b = a;</span><br><span class="line">a.x = a = &#123; <span class="attr">n</span>: <span class="number">2</span> &#125;;</span><br><span class="line"><span class="built_in">console</span>.log(a.x);</span><br><span class="line"><span class="built_in">console</span>.log(b);</span><br></pre></td></tr></table></figure>

<p>整个过程的对象只有两个{n:1}和{n:2}下面分别称为 A 对象和 B 对象 然后他们之间相互赋值和增加属性，记住仅有两个引用地址</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1.</span>首先将&#123;<span class="attr">n</span>:<span class="number">1</span>&#125;对象赋值给a变量，在将a赋值给b，这是一层浅拷贝的过程，所以a和b变量都指向同一个对象&#123;<span class="attr">n</span>:<span class="number">1</span>&#125;</span><br><span class="line"><span class="number">2.</span>由于点运算符优先于赋值符所以先在&#123;n：<span class="number">1</span>&#125;对象中创建了一个x属性 &#123;<span class="attr">n</span>:<span class="number">1</span>， x：（暂时无值）&#125; 这个过程 因为运算符优先级的原因 赋值符号还未执行的时候就已经完成了</span><br><span class="line"><span class="number">3.</span>再然后执行 a = &#123;<span class="attr">n</span>: <span class="number">2</span>&#125; 此时a的引用地址发生了改变 从对象&#123;<span class="attr">n</span>:<span class="number">1</span>&#125;指向了&#123;<span class="attr">n</span>:<span class="number">2</span>&#125;</span><br><span class="line"><span class="number">4.</span>a.x此时指向的是 &#123;<span class="attr">n</span>:<span class="number">1</span>， x：（暂时无值）&#125;对象 然后将&#123;n：<span class="number">2</span>&#125; 赋值给了a.x属性 此时A对象&#123;<span class="attr">n</span>:<span class="number">1</span>， x：&#123;n：<span class="number">2</span>&#125;&#125;</span><br><span class="line"><span class="number">5.</span>此时的a.x属性的父级对象a的初始引用地址是A对象 而A对象赋值给了b变量 所以你能在b.x中得到对象&#123;n：<span class="number">2</span></span><br><span class="line"><span class="number">6.</span>原对象地址因无人引用他 被垃圾回收机制回收了 对象被赋值了B对象&#123;n：<span class="number">2</span>&#125; 所以原有的a.x为<span class="literal">undefined</span></span><br></pre></td></tr></table></figure>

<p>.运算优先于= 赋值运算，因此此处赋值可理解为</p>
<ul>
<li>声明 a 对象中的 x 属性，用于赋值，此时 b 指向 a，同时拥有未赋值的 x 属性</li>
<li>对 a 对象赋值，此时变量名 a 改变指向到对象{n:2}</li>
<li>对于步骤 1 中的 x 属性，也即是指向对象的 x 属性，也即 b 指向对象的 x 属性赋值</li>
</ul>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line">a =&gt; &#123;<span class="attr">n</span>:<span class="number">2</span>&#125;</span><br><span class="line">b =&gt; &#123;<span class="attr">n</span>:<span class="number">1</span>,<span class="attr">x</span>:&#123;<span class="attr">n</span>:<span class="number">2</span>&#125;&#125;</span><br></pre></td></tr></table></figure>

<p>另一种说法</p>
<figure class="highlight js"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><span class="line"><span class="number">1.</span>多次赋值与顺序无关，是同时进行赋值的</span><br><span class="line"><span class="number">2.</span>每个节点的变量最终赋值的值取决去最后一个等号的右边值</span><br><span class="line"><span class="number">3.</span>如果赋值是引用类型，则最终指向的是同一个对象</span><br></pre></td></tr></table></figure>

<p>a.x 引用的是堆内存里面的属性变量，在解析时会直接对这个地址的变量进行操作，而 a=访问变量栈内存里面这个变量名，所以进行赋值操作并不会影响前者，因为解析这个语句时一次性全部读取在进行赋值，而不是分段读取赋值的。所以就解释了与顺序无关，所有的属性都将会被赋值到最右的值。</p>
<p>小结：</p>
<p>js 变量可以保存两种类型的值：原始值和引用值。原始值可能是以下 6 种原始数据类型之一：undefined、null、Boolean、Number、String 和 Symbol.原始值和引用值有以下特点</p>
<ul>
<li>原始值大小固定，因此保存在栈内存上，它由系统自动释放</li>
<li>从一个变量到另一个变量复制原始值会创建该值的第二个副本</li>
<li>引用值是对象，存储在堆内存上</li>
<li>包含引用值的变量实际上只包含指向相应对象的一个指针，而不是对象本身</li>
<li>从一个变量到另一个变量复制引用值只会复制指针，因此结果是两个变量都指向同一个对象</li>
<li>typeof 操作符可以确定值的原始类型，而 instanceof 操作符用于确保值的引用类型</li>
</ul>
<p>任何变量都存在于某个执行上下文中（也称作用域）。这个上下文（作用域）决定了变量的声明周期，以及它们可以访问代码的哪些部分。执行上下文可以总结如下</p>
<ul>
<li>执行上下文分为全局上下文呢、函数上下文和块级上下文</li>
<li>代码执行流每进入一个新的上下文，都会创建一个作用域链，用于搜索变量和函数</li>
<li>函数或块的局部上下文不仅可以访问自己作用域内的变量，而且也可以访问任何包含上下文乃至全局上下文中的变量。</li>
<li>全局上下文只能访问全局上下文的变量和函数，不能直接访问局部上下文种的任何数据。</li>
<li>变量的执行上下文用于确定什么时候释放内存</li>
</ul>
<p>js 是使用垃圾回收的编程语言，开发者不需要操心内存分配和回收。</p>
<ul>
<li>离开作用域的值会被自动标记为可回收，然后在垃圾回收期间被删除</li>
<li>主流的垃圾回收算法是标记清理，即先给当前不适用的值加上标记，在回来回收它们的内存</li>
<li>引用计数是另一种垃圾回收策略，需要记录值被引用了多少次。js 引擎不在使用这种算法，但某些版本的 ie 仍会受到影响。</li>
<li>引用计数在代码中存在循环引用时会出现问题</li>
<li>解除变量的引用不仅可以消除循环引用，而且对垃圾回收也有帮助。为促进内存回收，全局对象、全局对象的属性和循环引用都应该在不需要时解除引用</li>
</ul>

    </div>

    
    
    
      <div>
          
            
<div class="my_post_copyright">
  <script src="//cdn.bootcss.com/clipboard.js/1.5.10/clipboard.min.js"></script>
  
  <!-- JS库 sweetalert 可修改路径 -->
  <script src="https://cdn.bootcss.com/jquery/2.0.0/jquery.min.js"></script>
  <script src="https://unpkg.com/sweetalert/dist/sweetalert.min.js"></script>
  <p><span>本文标题:</span><a href="/codeblog/2021/02/27/%E5%8F%98%E9%87%8F%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%92%8C%E5%86%85%E5%AD%98/">Javascript基础（三）：javascript的变量、作用域与内存</a></p>
  <p><span>文章作者:</span><a href="/" title="访问  的个人博客"></a></p>
  <p><span>发布时间:</span>2021年02月27日 - 10:02</p>
  <p><span>最后更新:</span>2021年04月15日 - 09:04</p>
  <p><span>原始链接:</span><a href="/codeblog/2021/02/27/%E5%8F%98%E9%87%8F%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%92%8C%E5%86%85%E5%AD%98/" title="Javascript基础（三）：javascript的变量、作用域与内存">http://skycoin.gitee.io/codeblog/2021/02/27/%E5%8F%98%E9%87%8F%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%92%8C%E5%86%85%E5%AD%98/</a>
    <span class="copy-path"  title="点击复制文章链接"><i class="fa fa-clipboard" data-clipboard-text="http://skycoin.gitee.io/codeblog/2021/02/27/%E5%8F%98%E9%87%8F%E4%BD%9C%E7%94%A8%E5%9F%9F%E5%92%8C%E5%86%85%E5%AD%98/"  aria-label="复制成功！"></i></span>
  </p>
  <p><span>许可协议:</span><i class="fa fa-creative-commons"></i> 转载请保留原文链接及作者。</p>  
</div>
<script> 
    var clipboard = new Clipboard('.fa-clipboard');
    $(".fa-clipboard").click(function(){
      clipboard.on('success', function(){
        swal({   
          title: "",   
          text: '复制成功',
          icon: "success", 
          showConfirmButton: true
          });
    });
    });  
</script>



          
    </div>
        <div class="reward-container">
  <div>Donate comment here.</div>
  <button onclick="var qr = document.getElementById('qr'); qr.style.display = (qr.style.display === 'none') ? 'block' : 'none';">
    打赏
  </button>
  <div id="qr" style="display: none;">
      
      <div style="display: inline-block;">
        <img src="/codeblog/images/wechatpay.png" alt="若说 微信支付">
        <p>微信支付</p>
      </div>
      
      <div style="display: inline-block;">
        <img src="/codeblog/images/alipay.png" alt="若说 支付宝">
        <p>支付宝</p>
      </div>

  </div>
</div>

        

  <div class="followme">
    <p>欢迎关注我的其它发布渠道</p>

    <div class="social-list">

        <div class="social-item">
          <a target="_blank" class="social-link" href="/images/wechat_channel.jpg">
            <span class="icon">
              <i class="fab fa-weixin"></i>
            </span>

            <span class="label">WeChat</span>
          </a>
        </div>

        <div class="social-item">
          <a target="_blank" class="social-link" href="/atom.xml">
            <span class="icon">
              <i class="fa fa-rss"></i>
            </span>

            <span class="label">RSS</span>
          </a>
        </div>
    </div>
  </div>


      <footer class="post-footer">
          <div class="post-tags">
              <a href="/codeblog/tags/%E5%89%8D%E7%AB%AF/" rel="tag"># 前端</a>
          </div>

        


        
    <div class="post-nav">
      <div class="post-nav-item">
    <a href="/codeblog/2021/02/27/%E5%9F%BA%E6%9C%AC%E5%BC%95%E7%94%A8%E7%B1%BB%E5%9E%8B/" rel="prev" title="Javascript基础（四）：javascript基本引用类型">
      <i class="fa fa-chevron-left"></i> Javascript基础（四）：javascript基本引用类型
    </a></div>
      <div class="post-nav-item">
    <a href="/codeblog/2021/02/27/%E6%90%AD%E5%BB%BA%E5%8D%9A%E5%AE%A2/" rel="next" title="Javascript基础（一）：在html中引入script">
      Javascript基础（一）：在html中引入script <i class="fa fa-chevron-right"></i>
    </a></div>
    </div>
      </footer>
    
  </article>
  
  
  



          </div>
          
    <div class="comments" id="valine-comments"></div>

<script>
  window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }
</script>

        </div>
          
  
  <div class="toggle sidebar-toggle">
    <span class="toggle-line toggle-line-first"></span>
    <span class="toggle-line toggle-line-middle"></span>
    <span class="toggle-line toggle-line-last"></span>
  </div>

  <aside class="sidebar">
    <div class="sidebar-inner">

      <ul class="sidebar-nav motion-element">
        <li class="sidebar-nav-toc">
          文章目录
        </li>
        <li class="sidebar-nav-overview">
          站点概览
        </li>
      </ul>

      <!--noindex-->
      <div class="post-toc-wrap sidebar-panel">
          <div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-2"><a class="nav-link" href="#1-%E9%80%9A%E8%BF%87%E5%8F%98%E9%87%8F%E4%BD%BF%E7%94%A8%E5%8E%9F%E5%A7%8B%E5%80%BC%E4%B8%8E%E5%BC%95%E7%94%A8%E5%80%BC"><span class="nav-number">1.</span> <span class="nav-text">1.通过变量使用原始值与引用值</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#1-1-%E5%8E%9F%E5%A7%8B%E5%80%BC%E4%B8%8E%E5%BC%95%E7%94%A8%E5%80%BC"><span class="nav-number">1.1.</span> <span class="nav-text">1.1 原始值与引用值</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-1-1-%E5%8A%A8%E6%80%81%E5%B1%9E%E6%80%A7"><span class="nav-number">1.2.</span> <span class="nav-text">1.1.1 动态属性</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-1-2-%E5%A4%8D%E5%88%B6%E5%80%BC"><span class="nav-number">1.3.</span> <span class="nav-text">1.1.2 复制值</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-1-3-%E4%BC%A0%E9%80%92%E5%8F%82%E6%95%B0"><span class="nav-number">1.4.</span> <span class="nav-text">1.1.3 传递参数</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-1-4-%E7%A1%AE%E5%AE%9A%E7%B1%BB%E5%9E%8B"><span class="nav-number">1.5.</span> <span class="nav-text">1.1.4 确定类型</span></a><ol class="nav-child"><li class="nav-item nav-level-4"><a class="nav-link" href="#1-1-5-js-%E4%BB%A3%E7%A0%81%E8%BF%90%E8%A1%8C%E7%9A%84%E8%BF%87%E7%A8%8B"><span class="nav-number">1.5.1.</span> <span class="nav-text">1.1.5 js 代码运行的过程</span></a></li></ol></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-2-%E6%89%A7%E8%A1%8C%E4%B8%8A%E4%B8%8B%E6%96%87%E4%B8%8E%E4%BD%9C%E7%94%A8%E5%9F%9F"><span class="nav-number">1.6.</span> <span class="nav-text">1.2 执行上下文与作用域</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#2-%E7%90%86%E8%A7%A3%E6%89%A7%E8%A1%8C%E4%B8%8A%E4%B8%8B%E6%96%87"><span class="nav-number">2.</span> <span class="nav-text">2.理解执行上下文</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#1-3-%E4%BD%9C%E7%94%A8%E5%9F%9F%E9%93%BE%E5%A2%9E%E5%BC%BA"><span class="nav-number">2.1.</span> <span class="nav-text">1.3 作用域链增强</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#1-4-js-%E7%BC%96%E8%AF%91%E8%BF%87%E7%A8%8B-VO-AO"><span class="nav-number">2.2.</span> <span class="nav-text">1.4 js 编译过程 VO AO</span></a></li></ol></li><li class="nav-item nav-level-2"><a class="nav-link" href="#3-%E7%90%86%E8%A7%A3%E5%9E%83%E5%9C%BE%E5%9B%9E%E6%94%B6"><span class="nav-number">3.</span> <span class="nav-text">3.理解垃圾回收</span></a><ol class="nav-child"><li class="nav-item nav-level-3"><a class="nav-link" href="#3-1-1-%E6%A0%87%E8%AE%B0%E6%B8%85%E7%90%86"><span class="nav-number">3.1.</span> <span class="nav-text">3.1.1 标记清理</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-1-2-%E5%BC%95%E7%94%A8%E8%AE%A1%E6%95%B0"><span class="nav-number">3.2.</span> <span class="nav-text">3.1.2 引用计数</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-1-3-%E6%80%A7%E8%83%BD"><span class="nav-number">3.3.</span> <span class="nav-text">3.1.3 性能</span></a></li><li class="nav-item nav-level-3"><a class="nav-link" href="#3-1-4-%E5%86%85%E5%AD%98%E7%AE%A1%E7%90%86"><span class="nav-number">3.4.</span> <span class="nav-text">3.1.4 内存管理</span></a></li></ol></li></ol></div>
      </div>
      <!--/noindex-->

      <div class="site-overview-wrap sidebar-panel">
        <div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person">
    <img class="site-author-image" itemprop="image" alt="若说"
      src="https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=4083356047,3551369852&fm=26&gp=0.jpg">
  <p class="site-author-name" itemprop="name">若说</p>
  <div class="site-description" itemprop="description">立志完成前端知识体系的架构</div>
</div>
<div class="site-state-wrap motion-element">
  <nav class="site-state">
      <div class="site-state-item site-state-posts">
          <a href="/codeblog/archives/">
        
          <span class="site-state-item-count">5</span>
          <span class="site-state-item-name">日志</span>
        </a>
      </div>
      <div class="site-state-item site-state-categories">
            <a href="/codeblog/categories/">
          
        <span class="site-state-item-count">1</span>
        <span class="site-state-item-name">分类</span></a>
      </div>
      <div class="site-state-item site-state-tags">
        <span class="site-state-item-count">1</span>
        <span class="site-state-item-name">标签</span>
      </div>
  </nav>
</div>
  <div class="links-of-author motion-element">
      <span class="links-of-author-item">
        <a href="https://github.com/lovebxy" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;lovebxy" rel="noopener" target="_blank"><i class="fab fa-github fa-fw"></i>GitHub</a>
      </span>
      <span class="links-of-author-item">
        <a href="/codeblog/1490365616@qq.com" title="E-Mail → 1490365616@qq.com"><i class="fa fa-envelope fa-fw"></i>E-Mail</a>
      </span>
      <span class="links-of-author-item">
        <a href="https://weibo.com/yourname" title="Weibo → https:&#x2F;&#x2F;weibo.com&#x2F;yourname" rel="noopener" target="_blank"><i class="fab fa-weibo fa-fw"></i>Weibo</a>
      </span>
      <span class="links-of-author-item">
        <a href="https://plus.google.com/yourname" title="Google → https:&#x2F;&#x2F;plus.google.com&#x2F;yourname" rel="noopener" target="_blank"><i class="fab fa-google fa-fw"></i>Google</a>
      </span>
  </div>


  <div class="links-of-blogroll motion-element">
    <div class="links-of-blogroll-title"><i class="fa fa-link fa-fw"></i>
      Links
    </div>
    <ul class="links-of-blogroll-list">
        <li class="links-of-blogroll-item">
          <a href="http://yoursite.com/" title="http:&#x2F;&#x2F;yoursite.com" rel="noopener" target="_blank">test</a>
        </li>
    </ul>
  </div>

      </div>
        <div class="back-to-top motion-element">
          <i class="fa fa-arrow-up"></i>
          <span>0%</span>
        </div>

    </div>
  </aside>
  <div id="sidebar-dimmer"></div>


      </div>
    </main>

    <footer class="footer">
      <div class="footer-inner">
        

        

<div class="copyright">
  
  &copy; 
  <span itemprop="copyrightYear">2021</span>
  <span class="with-love">
    <i class="fa fa-heart"></i>
  </span>
  <span class="author" itemprop="copyrightHolder">若说</span>
</div>

<div class="theme-info">
  <div class="powered-by"></div>
  <span class="post-count">博客全站共25.5k字</span>
</div>
<script async src="//busuanzi.ibruce.info/busuanzi/2.3/busuanzi.pure.mini.js"></script>

<div class="powered-by">
  <i class="fa fa-user-md"></i>
  <span id="busuanzi_container_site_pv">
    本站访问量:<span id="busuanzi_value_site_pv"></span>次
  </span>
  <span class="post-meta-divider">|</span>
  <span id="busuanzi_container_site_uv">
    本站总访客量：<span id="busuanzi_value_site_uv"></span>人
  </span>
</div>

        








      </div>
    </footer>
  </div>

  
  <script src="/codeblog/lib/anime.min.js"></script>
  <script src="/codeblog/lib/velocity/velocity.min.js"></script>
  <script src="/codeblog/lib/velocity/velocity.ui.min.js"></script>

<script src="/codeblog/js/utils.js"></script>

<script src="/codeblog/js/motion.js"></script>


<script src="/codeblog/js/schemes/pisces.js"></script>


<script src="/codeblog/js/next-boot.js"></script>




  




  
<script src="/codeblog/js/local-search.js"></script>













  

  


<script>
NexT.utils.loadComments(document.querySelector('#valine-comments'), () => {
  NexT.utils.getScript('//unpkg.com/valine/dist/Valine.min.js', () => {
    var GUEST = ['nick', 'mail', 'link'];
    var guest = 'nick,mail,link';
    guest = guest.split(',').filter(item => {
      return GUEST.includes(item);
    });
    new Valine({
      el         : '#valine-comments',
      verify     : true,
      notify     : false,
      appId      : 'DbTfuJfplEWGguVvLyabTu99-gzGzoHsz',
      appKey     : 'hkLIIwmXwOd95taiLRxnUrjO',
      placeholder: "Just go go",
      avatar     : 'monsterid',
      meta       : guest,
      pageSize   : '10' || 10,
      visitor    : true,
      lang       : '' || 'zh-cn',
      path       : location.pathname,
      recordIP   : false,
      serverURLs : ''
    });
  }, window.Valine);
});
</script>

  <script type="text/javascript" src="/js/src/clicklove.js"></script>
  <script type="text/javascript" src="//cdn.bootcss.com/canvas-nest.js/1.0.0/canvas-nest.min.js"></script>

</body>
</html>
